﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using YAXLib;
using System.Reflection;
using System.Windows.Forms;

namespace SPLanLib
{
    /// <summary>
    /// Helps to read or write a language to your program.
    /// </summary>
    public class Crawler
    {
        /// <summary>
        /// Search in Environment.CurrentDirectory\\languages for languages files.
        /// </summary>
        /// <returns>The Names of the Languages.</returns>
        public static string[] GetAvaibleLanguages()
        {
            return Directory.GetFiles(Environment.CurrentDirectory + "\\languges", ".lang").Select(s =>
                s.Substring((Environment.CurrentDirectory + "\\languges\\").Length).Split('.')[0]).ToArray();
        }
        /// <summary>
        /// Search in Environment.CurrentDirectory\\languages for the language file.
        /// </summary>
        /// <param name="LanguagName">The search pattern.</param>
        /// <returns>The filepath of the language.</returns>
        public static string GetLangugeFileName(string LanguagName)
        {
            return Directory.GetFiles(Environment.CurrentDirectory + "\\languges", ".lang").ToList().Find(x => x.Contains(LanguagName));
        }
        /// <summary>
        /// Change all string propertys and strings of the fields, which are contained in the dictionary, in the given object.
        /// </summary>
        /// <param name="masterType">The object to manipulate.</param>
        /// <param name="Dic">The dictionary from a language.</param>
        public static void Write(object masterType,Dictionary<string,string> Dic)
        {
            _write(masterType, Dic);
        }
        private static void _write(object type,Dictionary<string,string> Dic)
        {
            if (type == null) return;
            var members = type.GetType().GetMembers(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).ToList();
            var tooltips = type.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).Where(x => x.FieldType == typeof(ToolTip)).ToList();
            var tooltip = new ToolTip();
            if (tooltips.Count == 1) tooltip = tooltips[0].GetValue(type) as ToolTip;
            var dic = Dic.Where(x => x.Key.Contains(type.GetType().FullName));
            var fields = members.Where(x=>x.MemberType == MemberTypes.Field).ToList();
            var properties = members.Where(x => x.MemberType == MemberTypes.Property).ToList();
            foreach(var d in dic)
            {
                var m = fields.Find(x => string.Format("{0}.{1}", type.GetType().FullName, x.Name) == d.Key);
                if (m != null)
                {
                    var f = m as FieldInfo;
                    if (typeof(Control).IsAssignableFrom(f.FieldType))
                    {
                        var o = f.GetValue(type) as Control;
                        o.Text = d.Value;
                        f.SetValue(type, o);
                    }
                    if (f.FieldType == typeof(ToolStripMenuItem))
                    {
                        var o = f.GetValue(type) as ToolStripMenuItem;
                        o.Text = d.Value;
                        f.SetValue(type, o);
                    }
                    if (f.FieldType.BaseType == typeof(DataGridViewColumn))
                    {
                        var o = f.GetValue(type) as DataGridViewColumn;
                        o.HeaderText = d.Value;
                        f.SetValue(type, o);
                    }
                }
                m = properties.Find(x => string.Format("{0}.{1}", type.GetType().FullName, x.Name) == d.Key);
                if (m == null) continue;
                var p = m as PropertyInfo;
                if(p.CanWrite) p.SetValue(type, d.Value, null);
            }
            foreach(var d in dic.Where(x=>x.Key.Contains("ToolTip")))
            {
                var m = fields.Find(x => string.Format("{0}.{1}", type.GetType().FullName, x.Name) == d.Key.Substring(0, d.Key.Length - 8));
                if (m == null)
                {
                    var sp = d.Key.Split('.').ToList();
                    sp.RemoveAt(sp.Count - 1);
                    sp.RemoveAt(sp.Count - 1);
                    m = fields.Find(x => string.Format("{0}.{1}", type.GetType().FullName, x.Name) == string.Join(".", sp.ToArray()));
                    if (m == null) continue;
                    var f2 = m as FieldInfo;
                    try
                    {
                        var s = f2.GetValue(type) as SplitContainer;
                        if(d.Key.Split('.')[d.Key.Split('.').Length - 2] == "Panel1") tooltip.SetToolTip(s.Panel1, d.Value);
                        else tooltip.SetToolTip(s.Panel2, d.Value);
                    }
                    catch { }
                    continue;
                }
                var f = m as FieldInfo;
                try
                {
                   tooltip.SetToolTip(f.GetValue(type) as Control,d.Value);
                }
                catch { }
            }
        }
        /// <summary>
        /// For development. Search for all strings in the object and writes it to a dictionary.
        /// </summary>
        /// <param name="masterType">The type for search.</param>
        /// <param name="Dic">The destinay</param>
        /// <param name="onlyifcontainsth">Write it only to the dictionary if the string is not empty</param>
        public static void Read(object masterType, Dictionary<string, string> Dic,bool onlyifcontainsth = false)
        {
            var dic = new Dictionary<string, string>();
            _read(masterType, dic,onlyifcontainsth);
            foreach (var d in dic) if (!Dic.ContainsKey(d.Key)) Dic.Add(d.Key, d.Value);
        }
        private static void _read(object type, Dictionary<string, string> Dic, bool onlyifcontainsth)
        {
            if (type == null) return;
            var members = type.GetType().GetMembers(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
            var tooltips = type.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Where(x => x.FieldType == typeof(ToolTip)).ToList();
            var tooltip = new ToolTip();
            if (tooltips.Count == 1) tooltip = tooltips[0].GetValue(type) as ToolTip;
            foreach(var m in members)
            {
                if (m.MemberType == System.Reflection.MemberTypes.Method || m.MemberType == MemberTypes.Constructor) continue;
                if (m.GetCustomAttributes(true).Contains(new SPLanLibDont())) continue;
                if(m.MemberType == System.Reflection.MemberTypes.Property)
                {
                    var p = m as PropertyInfo;
                    try
                    {
                        if (p.PropertyType == typeof(string)) Dic.Add(string.Format("{0}.{1}", type.GetType().FullName, m.Name), p.GetValue(type, null).ToString());
                    }
                    catch (NullReferenceException) { /*Dic.Add(string.Format("{0}.{1}", type.GetType().FullName, m.Name),string.Empty);*/}
                    
                }
                if (m.MemberType == MemberTypes.Field)
                {
                    var f = m as FieldInfo;
                    try
                    {
                        if ((typeof(Control).IsAssignableFrom(f.FieldType)))
                        {
                            var s = (f.GetValue(type) as Control).Text;
                            if (s != string.Empty) Dic.Add(string.Format("{0}.{1}", type.GetType().FullName, m.Name), s);
                            else if (!onlyifcontainsth) Dic.Add(string.Format("{0}.{1}", type.GetType().FullName, m.Name), s);
                        }
                        if ((typeof(ToolStripMenuItem).IsAssignableFrom(f.FieldType)))
                        {
                            var s = (f.GetValue(type) as ToolStripMenuItem).Text;
                            if (s != string.Empty) Dic.Add(string.Format("{0}.{1}", type.GetType().FullName, m.Name), s);
                            else if (!onlyifcontainsth) Dic.Add(string.Format("{0}.{1}", type.GetType().FullName, m.Name), s);
                        }
                        try
                        {
                            var t = tooltip.GetToolTip(f.GetValue(type) as Control);
                            if (t != string.Empty) Dic.Add(string.Format("{0}.{1}.ToolTip", type.GetType().FullName, m.Name), t);
                        }
                        catch { }
                        if (f.FieldType == typeof(SplitContainer))
                        {
                            var container = f.GetValue(type) as SplitContainer;
                            try
                            {
                                var t = tooltip.GetToolTip(container.Panel1);
                                if (t != string.Empty) Dic.Add(string.Format("{0}.{1}.Panel1.ToolTip", type.GetType().FullName, m.Name), t);
                            }
                            catch { }
                            try
                            {
                                var t = tooltip.GetToolTip(container.Panel2);
                                if (t != string.Empty) Dic.Add(string.Format("{0}.{1}.Panel2.ToolTip", type.GetType().FullName, m.Name), t);
                            }
                            catch { }
                        }
                        if (f.FieldType.BaseType == typeof(DataGridViewColumn)) Dic.Add(string.Format("{0}.{1}", type.GetType().FullName, m.Name), (f.GetValue(type) as DataGridViewColumn).HeaderText);
                    }
                    catch (NullReferenceException) { }
               }
            }
        }
    }
    /// <summary>
    /// Contains Infos to change the language.
    /// </summary>
    public class Language
    {
        /// <summary>
        /// This contains all language strings. Key is the full Name (with Namespace) of the string.
        /// </summary>
        [YAXDictionary(EachPairName = "ItemInfo", KeyName = "Key", ValueName = "Value",
                   SerializeKeyAs = YAXNodeTypes.Attribute,
                   SerializeValueAs = YAXNodeTypes.Attribute)]
        public Dictionary<string, string> dictionary { get; set; }
        /// <summary>
        /// A Key that discribes the Language, e.g. "de", "en".
        /// </summary>
        /// <example>"de", "en"</example>
        [YAXAttributeForClass()]
        public string LanguageKey { get; set; }
        /// <summary>
        /// The Name of the Language.
        /// </summary>
        [YAXAttributeForClass()]
        public string LanguageName { get; set; }
        /// <summary>
        /// Writes the Language with the help of YAXLib as an XML to the filepath. For development.
        /// </summary>
        /// <param name="filename">The destination of the XML file.</param>
        public void WriteToFile(string filename)
        {
            var ser = new YAXSerializer(typeof(Language), YAXExceptionHandlingPolicies.DoNotThrow);
            ser.SerializeToFile(this, filename);
        }
        /// <summary>
        /// Writes the Language with the help of YAXLib as an XML to the Environment.CurrentDirectory\\languages\\LanguageName.LanguageKey.lang . For development.
        /// </summary>
        public void WriteToFile()
        {
            WriteToFile(string.Format("{0}\\languages\\{1}.{2}.lang", Environment.CurrentDirectory,LanguageName, LanguageKey));
        }
        /// <summary>
        /// Reads the Language with the help of YAXLib as an XML from the filepath.
        /// </summary>
        /// <param name="filename">The source of the languagefile.</param>
        /// <returns>True: No Parsing Erros. False: Parsing Erros.</returns>
        public bool ReadFromFile(string filename)
        {
            var ser = new YAXSerializer(typeof(Language), YAXExceptionHandlingPolicies.DoNotThrow);
            var temp = ser.DeserializeFromFile(filename) as Language;
            if (ser.ParsingErrors.ContainsAnyError) return false;
            this.dictionary = temp.dictionary;
            this.LanguageKey = temp.LanguageKey;
            this.LanguageName = temp.LanguageName;
            return true;
        }
        /// <summary>
        /// Creates a new Instance.
        /// </summary>
        public Language()
        {
            dictionary = new Dictionary<string, string>();
        }
    }
    /// <summary>
    /// When the property or the field should not read by the Lib.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
    public class SPLanLibDont : Attribute { }
}
